/**
 * Copyright (c) 2009 blurredVision
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
 * associated documentation files (the "Software"), to deal in the Software without restriction, 
 * including without limitation the rights to use, copy, modify, merge, publish, distribute, 
 * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is 
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all copies or 
 * substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT 
 * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 
 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT 
 * OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
#include "StdAfx.h"
#include "ResourceFactory.h"
#include "core/core.h"
#include "dx/Model.h"
#include "dx/Texture.h"
#include "dx/CubeMap.h"
#include "dx/DXShaderEffect.h"
#include "dx/BitmapFont.h"
#include "dx/TrueTypeFont.h"

#include <algorithm>
#include <sstream>

ResourceFactory* g_pResourceFactory = NULL;

ResourceFactory* coreGetResourceFactory()
{
	if (!g_pResourceFactory) 
		g_pResourceFactory = new ResourceFactory();
	return g_pResourceFactory;
}


ResourceFactory::ResourceFactory(void)
{
	iRendererType = coreGetRendererType();
	m_mResources.clear();
}

void ResourceFactory::Destroy()
{
	g_pResourceFactory = NULL;
	delete this;
}

ResourceFactory::~ResourceFactory(void)
{
	freeAll();
}

void ResourceFactory::releaseAll()
{
	res_map::iterator it;
	IResource* resource = NULL;
	for (it = m_mResources.begin(); it != m_mResources.end(); ++it) {
		resource = it->second;
		resource->release();
	}
}

void ResourceFactory::resetAll()
{
	res_map::iterator it;

	IResource* resource = NULL;
	for (it = m_mResources.begin(); it != m_mResources.end(); ++it) {
		resource = it->second;
		resource->reset();
	}
}

void ResourceFactory::resetAllShaderEffects()
{
	res_map::iterator it;

	IResource* resource = NULL;
	for (it = m_mResources.begin(); it != m_mResources.end(); ++it) {
		resource = it->second;
		if (resource->getType() == RESTYPE_SHADEREFFECT)
			resource->reset();
	}
}

bool ResourceFactory::freeRes(IResource* resource)
{
	res_map::iterator it;
	for (it = m_mResources.begin(); it != m_mResources.end(); ++it) {
		if (it->second == resource) {
			it->second->destroy();
			it = m_mResources.erase(it);
			return true;
		}
	}
	return false;
}

void ResourceFactory::freeAll() 
{ 
 	res_map::iterator it;
	IResource* resource = NULL;
	for (it = m_mResources.begin(); it != m_mResources.end(); ++it) {
		resource = it->second;
		DELETE_POINTER(resource);
	}
	m_mResources.clear();
};

IModel* ResourceFactory::loadModel(const std::string &filename) 
{
	IModel* resource = NULL;
	synchronized(m_Mutex) {	
		resource = dynamic_cast<IModel*>(m_mResources[filename]);
		
		if (!resource) {
			if (iRendererType == CORE_DIRECTX) 
				resource = new directx::Model();

			if (!resource->load(filename)) {
				return 0;
			}
			m_mResources[filename] = resource;
		}
	}
	return resource;
}

model::IModelBase* ResourceFactory::loadCustomModel(const std::string &filename)
{
	model::IModelBase* resource = NULL;
	synchronized(m_Mutex) {
		resource = dynamic_cast<model::IModelBase*>( m_mResources[filename] );

		if (!resource) {
			resource = model::IModelBase::Load(filename);
 			m_mResources[filename] = resource;
			//return resource;
		}
	}
	// Make a copy of the object
	return dynamic_cast<model::IModelBase*>( resource->copy() );
}

ITexture* ResourceFactory::loadTexture(const std::string &filename, bool dynamic, eFormat format) 
{
	ITexture* resource = NULL;
	synchronized(m_Mutex) {
		resource = dynamic_cast<ITexture*>(m_mResources[filename]);

		if (!resource) {
			if (iRendererType == CORE_DIRECTX) 
				resource = new directx::Texture();

			m_mResources[filename] = resource;
		}

		if ( !resource->isLoaded() ) {
			resource->load(filename, dynamic, format);
			resource->addRef();
		}		
	}
	return resource;
}

ICubeMap* ResourceFactory::loadCubeMap(const std::string &filename, bool dynamic, eFormat format)
{
	ICubeMap* resource = NULL;
	synchronized(m_Mutex) {
		resource = dynamic_cast<ICubeMap*>(m_mResources[filename]);

		if (!resource) {
			if (iRendererType == CORE_DIRECTX) 
				resource = new directx::CubeMap();
		
			m_mResources[filename] = resource;
		}

		if ( !resource->isLoaded() ) {
			resource->load(filename, dynamic, format);
		}
		// Increase reference count
		resource->addRef();
	}	
	return resource;
}

ITexture* ResourceFactory::createNormalMap(const std::string &filename)
{
	directx::Texture* resource = NULL;
	synchronized(m_Mutex) {
		resource = new directx::Texture();
		resource->computeNormalMap( filename );

		std::ostringstream sstr;
		sstr << rand();
		while (m_mResources[sstr.str()] != NULL) {
			sstr << rand();
		}
		m_mResources[sstr.str()] = resource;
		resource->addRef();
	}
	return dynamic_cast<ITexture*>(resource);
}

IShaderEffect* ResourceFactory::loadShaderEffect(const std::string &filename) 
{
	IShaderEffect* resource = NULL;
	synchronized(m_Mutex) {
		resource = dynamic_cast<IShaderEffect*>(m_mResources[filename]);
		
		if (!resource) {
			if (iRendererType == CORE_DIRECTX) 
				resource = new directx::DXShaderEffect();
		}

		if ( !resource->isLoaded() ) {
			if (!resource->load(filename)) {
				// destroy the object again and set the pointer to 0
				DELETE_POINTER(resource);
			} else {
				m_mResources[filename] = resource;
				resource->addRef();
			}
		}
	}
	return resource;
}

IFont* ResourceFactory::loadBitmapFont(const std::string &filename, const std::string &metrics, int size) 
{
	directx::BitmapFont* resource = NULL;
	synchronized(m_Mutex) {
		resource = dynamic_cast<directx::BitmapFont*>(m_mResources[filename+metrics]);

		if (!resource) {
			if (iRendererType == CORE_DIRECTX) 
				resource = new directx::BitmapFont();

			if (!resource->load(filename, size)) {
				return 0;
			}

			if (metrics.length() != 0) {
				resource->loadMetrics( metrics );
			}

			m_mResources[filename+metrics] = resource;
		}
	}
	return resource;
}

IFont* ResourceFactory::loadTrueTypeFont(const std::string &filename, int size, int style) 
{
	IFont* resource = NULL;
	synchronized(m_Mutex) {
		std::ostringstream sstr;
		sstr << filename << "-" << size << "-" << style;
		resource = dynamic_cast<IFont*>(m_mResources[sstr.str()]);

		if (!resource) {
			if (iRendererType == CORE_DIRECTX) 
				resource = new directx::TrueTypeFont();

			if (!resource->load(filename, size, style)) {
				return 0;
			}
			m_mResources[sstr.str()] = resource;
		}		
	}
	return resource;
}
